#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <netinet/tcp.h>

#include "InetAddress.h"

void setnonblocking(int fd) {
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
}

void str_toupper(char *buf, int len) {
    for (int i = 0; i < len; ++i)
        if (buf[i] >= 'a' && buf[i] <= 'z')
            buf[i] -= 32;
}

int main(int argc, char* argv[]) {
    if (argc != 3) {
        printf("usage: ./tcpepoll ip port\n");
        return -1;
    }

    int listenfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
    if (listenfd < 0) {
        perror("socket() failed");
        return -1;
    }

    setnonblocking(listenfd);
    int opt = 1;
    // 设置端口复用，允许在 TIME_WAIT 状态下快速重启服务器绑定同一个端口
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    // 禁用 Nagle 算法，保证小数据包立即发送，减少延迟
    setsockopt(listenfd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
    setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));

    

    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(argv[1]);
    servaddr.sin_port = htons(atoi(argv[2]));

    if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind() failed");
        close(listenfd);
        return -1;
    }

    if (listen(listenfd, 128) != 0) {
        perror("listen() failed");
        close(listenfd);
        return -1;
    }

    int epollfd = epoll_create(1);
    struct epoll_event ev, evs[1024];

    ev.data.fd = listenfd;
    ev.events = EPOLLIN | EPOLLET;  // 边缘触发
    epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev);

    while (1) {
        int infds = epoll_wait(epollfd, evs, 1024, -1);
        if (infds < 0) {
            perror("epoll_wait() failed");
            break;
        }

        for (int i = 0; i < infds; i++) {
            int curfd = evs[i].data.fd;

            if (curfd == listenfd) {
                // 接受所有新连接（边缘触发必须循环）
                while (1) {
                    struct sockaddr_in clientaddr;
                    socklen_t len = sizeof(clientaddr);
                    // 接受一个客户端连接
                    // listenfd: 监听套接字
                    // clientaddr: 存放客户端地址信息
                    // len: 客户端地址长度
                    // SOCK_NONBLOCK: 返回的 clientfd 设置为非阻塞
                    int clientfd = accept4(listenfd, (struct sockaddr*)&clientaddr, &len, SOCK_NONBLOCK);

                    // clientfd:
                    //   >0: 成功，返回客户端套接字，用于读写
                    //   -1: 失败，检查 errno
                    //      EAGAIN/EWOULDBLOCK: 非阻塞模式下没有连接可接受
                    //      其他 errno: 出错

                    if (clientfd < 0) {
                        if (errno == EAGAIN || errno == EWOULDBLOCK)
                            break;  // 没有更多连接
                        perror("accept() failed");
                        break;
                    }

                    printf("New client connected: %s:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

                    struct epoll_event cev;
                    cev.data.fd = clientfd;
                    cev.events = EPOLLIN | EPOLLET;
                    epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &cev);
                }
            } else {
                // 处理客户端数据（边缘触发必须读到EAGAIN）
                char buf[1024];
                while (1) {
                    ssize_t n = read(curfd, buf, sizeof(buf));
                    if (n < 0) {
                        if (errno == EAGAIN || errno == EWOULDBLOCK)
                            break;  // 所有数据读完
                        perror("read() error");
                        close(curfd);
                        epoll_ctl(epollfd, EPOLL_CTL_DEL, curfd, NULL);
                        break;
                    } else if (n == 0) {
                        printf("Client disconnected, fd=%d\n", curfd);
                        close(curfd);
                        epoll_ctl(epollfd, EPOLL_CTL_DEL, curfd, NULL);
                        break;
                    } else {
                        str_toupper(buf, n);  // 转大写
                        write(curfd, buf, n); // 写回
                    }
                }
            }
        }
    }

    close(listenfd);
    return 0;
}
